use clap::Clap;
use serde::Deserialize;
use std::collections::HashMap;
use std::fs::File;
use std::io::Read;
use std::process::Command;
use toml;

#[derive(Clap)]
#[clap(version = "0.1.0", author = "jiacai2050 <jiacai2050@gmail.com>")]
struct Opts {
    /// Sets a custom config file
    #[clap(short, long, default_value = "default.toml")]
    config: String,
    /// A level of verbosity, and can be used multiple times
    #[clap(short, long, parse(from_occurrences))]
    verbose: i32,
}

#[derive(Debug, Deserialize)]
struct Program {
    user: String,
    command: String,
    autostart: bool,
    autorestart: bool,
    stderr_logfile: Option<String>,
    stdout_logfile: Option<String>,
    stopsignal: Option<Vec<String>>,
}

#[derive(Debug, Deserialize)]
struct ProgramConfig {
    program: HashMap<String, Program>,
}

fn main() {
    println!("Process Manager in Rust.");

    let opts: Opts = Opts::parse();
    println!("Value for config: {}", opts.config);
    match opts.verbose {
        0 => println!("No verbose info"),
        1 => println!("Some verbose info"),
        2 => println!("Tons of verbose info"),
        3 | _ => println!("Don't be crazy"),
    }

    let mut f = File::open(opts.config).unwrap();
    let mut toml_str = String::new();
    f.read_to_string(&mut toml_str).unwrap();
    let config: ProgramConfig = toml::from_str(&toml_str).unwrap();

    for (prog_name, prog_conf) in &config.program {
        let prog_ret = start_program(prog_conf);
        println!("{} prog_ret {:?}", prog_name, prog_ret);
    }
}

type Pid = u32;
fn start_program(program_conf: &Program) -> Result<Pid, ()> {
    let cmd = program_conf.command.split(" ").collect::<Vec<_>>();
    let prog_name = cmd[0];
    let prog_args = &cmd[1..];
    println!(
        "start program name = {:?}, args = {:?}",
        prog_name, prog_args
    );

    let prog_out = Command::new(prog_name)
        .args(prog_args)
        .output()
        .expect("failed to start program");

    let stdout = String::from_utf8_lossy(&prog_out.stdout);
    let stderr = String::from_utf8_lossy(&prog_out.stderr);

    println!("stdout = {}, stderr = {}", stdout, stderr);

    Ok(1)
}
